package com.d.lib.ui.view.lrc;

import com.d.lib.ui.view.AnimatorValueUtils;
import com.d.lib.ui.view.AttrUtils;
import com.d.lib.ui.view.LogUtil;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import ohos.aafwk.ability.Ability;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrHelper;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ScrollHelper;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.TextAlignment;
import ohos.app.Context;
import ohos.multimodalinput.event.MmiPoint;
import ohos.multimodalinput.event.TouchEvent;
import static ohos.data.search.schema.PhotoItem.TAG;

/**
 * LrcView
 * https://github.com/android-lili/CustomLrcView-master
 * Edited by D on 2017/5/16.
 */
public class LrcView extends Component implements ILrcView, Component.DrawTask,
    Component.EstimateSizeListener, Component.TouchEventListener {
  private final String DEFAULT_TEXT = "畅音乐，享自由";
  /***移动一句歌词的持续时间***/
  private final int DURATION_AUTO_SCROLL = 1500;
  /***停止触摸 如果View需要滚动的持续时间***/
  private final int DURATION_RESET = 400;

  private final float SIZE_TEXT_DEFAULT; // Variables 文字大小
  private final float SIZE_TEXT_CUR_DEFAULT;
  private final float SIZE_TIME_DEFAULT;
  private final float PADDING_DEFAULT;
  private final float PADDING_TIME_DEFAULT;

  private float MIN_SCALE = 0.7f; // 最小缩放
  private float MAX_SCALE = 1.7f; // 最大缩放

  private int mWidth;
  private int mHeight;
  private Context mContext;
  private ScrollHelper mScroller;
  private List<LrcRow> mLrcRows;
  private int mTouchSlop;
  private float mDX, mDY; // ActionDown的坐标(dx,dy)
  private float mLastY; // TouchEvent最后一次坐标(lastX,lastY)
  private boolean mDVaild;
  private boolean mCanDrag;

  private int mColorText;
  private int mColorTextCur;
  private int mColorTime;
  private float mSizeText; // Variables 文字大小
  private float mSizeTextCur;
  private float mSizeTime;
  private float mPadding;
  private float mPaddingTime;
  private Paint mPaint; // 仅用于普通文字的画笔
  private Paint mPaintCur; // 仅用于当前高亮文字的画笔
  private Paint mPaintLine; // 仅用于画线的画笔

  private int mRowCount;
  private int mLastRow = -1;
  private int mCurRow = -1;

  private int mRowHeight; // One row height(text+padding)
  private int mOffsetY;

  private AnimatorValue mAnimation;
  private float mTextOffsetX; // 当前歌词水平滚动偏移
  private float mScaleFactor; // 进度因子
  private boolean mWithLine;

  private OnClickListener mOnClickListener;
  private OnSeekChangeListener mOnSeekChangeListener;

  int scrollX = getScrollValue(Component.AXIS_X);


  public LrcView(Context context) {
    this(context, null);
  }

  public LrcView(Context context, AttrSet attrs) {
    this(context, attrs, null);

  }

  public LrcView(Context context, AttrSet attrs, String defStyleAttr) {
    super(context, attrs, defStyleAttr);
    addDrawTask(this::onDraw);
    setEstimateSizeListener(this::onEstimateSize);
    setTouchEventListener(this::onTouchEvent);

    mColorText = AttrUtils.getColorFromAttr(attrs, "lib_ui_view_lrc_textColor",
        Color.getIntColor("#4577B7"));
    mColorTextCur = AttrUtils.getColorFromAttr(attrs, "lib_ui_view_lrc_textColorCur",
        Color.getIntColor("#FF4081"));
    mColorTime = AttrUtils.getColorFromAttr(attrs, "lib_ui_view_lrc_timeColor",
        Color.getIntColor("#FF4081"));
    mSizeText = SIZE_TEXT_DEFAULT = AttrUtils.getDimensionFromAttr(attrs,
        "lib_ui_view_lrc_textSize", 15);
    mSizeTextCur = SIZE_TEXT_CUR_DEFAULT = AttrUtils.getDimensionFromAttr(attrs,
        "lib_ui_view_lrc_textSizeCur", 17);
    mSizeTime = SIZE_TIME_DEFAULT = AttrUtils.getDimensionFromAttr(attrs,
        "lib_ui_view_lrc_timeSize", 8);
    mPadding = PADDING_DEFAULT = AttrUtils.getDimensionFromAttr(attrs, "lib_ui_view_lrc_padding",
        17);
    MIN_SCALE = AttrUtils.getFloatFromAttr(attrs, "lib_ui_view_lrc_minScale", 0.7f);
    MAX_SCALE = AttrUtils.getFloatFromAttr(attrs, "lib_ui_view_lrc_maxScale", 1.7f);
    mPaddingTime = PADDING_TIME_DEFAULT = AttrHelper.vp2px(5, context);
    init(context);
  }

  @Override
  public void init(Context context) {
    mContext = context;
    mLrcRows = new ArrayList<>();
    mScroller = new ScrollHelper();

    mTouchSlop = 24;
    mPaint = new Paint();
    mPaint.setAntiAlias(true);
    mPaint.setTextAlign(TextAlignment.LEFT);
    mPaint.setColor(new Color(mColorText));

    mPaintCur = new Paint();
    mPaintCur.setAntiAlias(true);
    mPaintCur.setTextAlign(TextAlignment.LEFT);
    mPaintCur.setColor(new Color(mColorTextCur));

    mPaintLine = new Paint();
    mPaintLine.setAntiAlias(true);
    mPaintLine.setTextAlign(TextAlignment.LEFT);
    mPaintLine.setColor(new Color(mColorTime));

    resetValues();
    initAnim();
  }

  private void initAnim() {
    mAnimation = new AnimatorValue();
    mAnimation.setValueUpdateListener(new UpdateListener(this));

  }

  public static float getTextHeight(Paint p) {
    Paint.FontMetrics fm = p.getFontMetrics();
    return (float) ((Math.ceil(fm.descent - fm.top) + 2) / 2);
  }

  @Override
  public void onDraw(Component component, Canvas canvas) {
    if (mLrcRows.size() <= 0) {
      // 画默认的显示文字
      mPaint.setColor(new Color(mColorText));
      drawLine(canvas, mPaint, mHeight / 2 + getTextHeight(mPaint) / 2, DEFAULT_TEXT);
      return;
    }

    if (mRowHeight != 0) {
      mRowCount = mHeight / mRowHeight + 4; // 初始化将要绘制的歌词行数,因为不需要将所有歌词画出来
    }
    int minRaw = mCurRow - mRowCount / 2;
    int maxRaw = mCurRow + mRowCount / 2;
    minRaw = Math.max(minRaw, 0); // 处理上边界
    maxRaw = Math.min(maxRaw, mLrcRows.size() - 1); // 处理下边界
    // 实现渐变的最大歌词行数
    int count = Math.max(maxRaw - mCurRow, mCurRow - minRaw);
    // 两行歌词间字体颜色变化的透明度
    int alpha = 0;
    if (count != 0) {
      alpha = (0xFF - 0x11) / count;
    }

    for (int i = minRaw; i <= maxRaw; i++) {
      String text = mLrcRows.get(i).getContent();//获取到高亮歌词
      if (mCurRow == i) {
        // 画高亮歌词
        // 因为有缩放效果，所有需要动态设置歌词的字体大小
        float size = mSizeTextCur + (mSizeTextCur - mSizeText) * mScaleFactor;
        mPaintCur.setTextSize((int) size);
        // 用画笔测量歌词的宽度
        float textWidth = mPaintCur.measureText(text);
        if (textWidth > mWidth) {
          // 如果歌词宽度大于view的宽，则需要动态设置歌词的起始x坐标，以实现水平滚动
          canvas.drawText(mPaintCur, text, mTextOffsetX, 860);
        } else {
          // 如果歌词宽度小于view的宽，则让歌词居中显示
          float rowX = (mWidth - textWidth) / 2;
          canvas.drawText(mPaintCur, text, rowX, 860);
        }

        // 画时间线和时间
        if (mWithLine) {
          canvas.drawText(mPaintLine, mLrcRows.get(mCurRow).getTimeStr(), 0, 860);
          float stopX = textWidth < mWidth - mPaddingTime ?
              (mWidth - textWidth) / 2 - mPaddingTime : 0;
          canvas.drawLine(0, 860, stopX, 860, mPaintLine);
          float startX = textWidth < mWidth - mPaddingTime ?
              mWidth - (mWidth - textWidth) / 2 + mPaddingTime : mWidth;
          canvas.drawLine(startX, 860, mWidth, 860, mPaintLine);
        }
      } else {
        if (i == mLastRow) {
          // 画高亮歌词的上一句
          // 因为有缩放效果，所有需要动态设置歌词的字体大小
          float size = mSizeTextCur - (mSizeTextCur - mSizeText) * mScaleFactor;
          mPaint.setTextSize((int) size);
        } else {
          // 画其他的歌词
          mPaint.setTextSize((int) mSizeText);
        }
        float textWidth = mPaint.measureText(text);
        float rowX = (getWidth() - textWidth) / 2;
        // 如果计算出的cx为负数,将cx置为0(实现：如果歌词宽大于view宽，则居左显示，否则居中显示)
        if (rowX < 0) {
          rowX = 0;
        }
        // 实现颜色渐变
        mPaint.setColor(new Color(0x1000000 * (255 - Math.abs(i - mCurRow) * alpha) + mColorText));
        canvas.drawText(mPaint, text, rowX, 860 + (i - mCurRow) * mRowHeight);
      }
    }
  }

  @Override
  public boolean onEstimateSize(int widthMeasureSpec, int heightMeasureSpec) {
    mWidth = EstimateSpec.getSize(widthMeasureSpec);
    mHeight = EstimateSpec.getSize(heightMeasureSpec);
    setEstimatedSize(EstimateSpec.getSizeWithMode(mWidth, EstimateSpec.PRECISE),
        EstimateSpec.getSizeWithMode(mHeight, EstimateSpec.PRECISE));
    return true;
  }

  @Override
  public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
    scrollX = getScrollValue(Component.AXIS_X);
    MmiPoint point = touchEvent.getPointerPosition(0);
    if (mLrcRows.size() <= 0) {
      return false;
    }
    float eX = point.getX();
    float eY = point.getY();
    switch (touchEvent.getAction()) {
      case TouchEvent.PRIMARY_POINT_DOWN:
        mDX = point.getX();
        mDY = mLastY = point.getY();
        mDVaild = true;
        return true;
      case TouchEvent.POINT_MOVE:
        if (mDVaild && (Math.abs(eX - mDX) > mTouchSlop || Math.abs(eY - mDY) > mTouchSlop)) {
          // 点击无效
          mDVaild = false;
        }
        if (!mCanDrag && Math.abs(eY - mDY) > mTouchSlop
            && Math.abs(eY - mDY) > Math.abs(eX - mDX)) {
          mCanDrag = true;
          forceFinished();
          stopHorizontalScrollLrc();
          mScaleFactor = 1;
          mWithLine = true;
        }
        if (mCanDrag) {
          // 偏移量
          float offset = eY - mLastY;
          scrollBy(scrollX, -(int) offset);
          int curRow = (int) ((getScrollValue(Component.AXIS_Y) + mRowHeight / 2) / mRowHeight);
          curRow = Math.max(curRow, 0);
          curRow = Math.min(curRow, mLrcRows.size() - 1);
          seekTo(mLrcRows.get(curRow).getTime(), true);
        }
        mLastY = point.getY();
        return true;
      case TouchEvent.PRIMARY_POINT_UP:
      case TouchEvent.CANCEL:
        if (mDVaild) {
          if (mOnClickListener != null) {
            mOnClickListener.onClick();
          }
        } else {
          if (mCanDrag) {
            if (mLrcRows.size() > 0 && mCurRow < mLrcRows.size()
                && mOnSeekChangeListener != null) {
              mOnSeekChangeListener.onProgressChanged(mLrcRows.get(mCurRow).getTime());
            }
          }
        }
        if (getScrollValue(Component.AXIS_Y) < 0) {
          smoothScrollTo(0, DURATION_RESET);
        } else {
          int offset = (int) (mLrcRows.size() * mRowHeight - mPadding);
          if (getScrollValue(Component.AXIS_Y) > offset) {
            smoothScrollTo(offset, DURATION_RESET);
          }
        }
        resetTouch();
        invalidate();
        return true;
    }
    return true;
  }

  private void drawLine(Canvas canvas, Paint paint, float y, String text) {
    float textWidth = paint.measureText(text);
    float textX = (mWidth - textWidth) / 2;
    if (textX < 0) {
      textX = 0;
    }
    canvas.drawText(paint, text, textX, y);
  }

  private void smoothScrollTo(int dstY, int duration) {
    float oldScrollY = getScrollValue(Component.AXIS_Y);
    float offset = dstY - oldScrollY;
    mScroller.startScroll((int) scrollX, (int) oldScrollY, (int) scrollX, (int) offset);
    invalidate();
  }

  private void forceFinished() {
    stopHorizontalScrollLrc();
    if (!mScroller.isOverScrolled()) {
      mScroller.isOverScrolled();
    }
  }

  private void resetTouch() {
    mDX = mDY = 0;
    mDVaild = mCanDrag = false;
    mWithLine = false;
  }
  /**
   * 设置最大缩放
   */
  public void setMAX_SCALE(int scale) {
    MAX_SCALE = scale;
  }

  /**
   * 设置最小缩放
   */
  public void setMIN_SCALE(int scale) {
    MIN_SCALE = scale;
  }

  /**
   * 设置填充
   */
  public void setmPadding(float padding) {
    mPadding = padding;
  }

  /**
   * 设置文字颜色
   */
  public void setmColorText(int color) {
    mColorText = color;
  }

  /**
   * 设置选中颜色
   */
  public void setmColorTextCur(int color) {
    mColorTextCur = color;
  }

  /**
   * 设置时间颜色
   */
  public void setmColorTime(int color) {
    mColorTime = color;
  }

  /**
   * 设置文字大小
   */
  public void setmSizeText(int size) {
    mSizeText = size;
  }

  /**
   * 设置选中文字大小
   */
  public void setmSizeTextCur(int size) {
    mSizeTextCur = size;
  }

  /**
   * 设置时间文字大小
   */
  public void setmSizeTime(int size) {
    mSizeTime = size;
  }

  @Override
  public void setLrcRows(List<LrcRow> lrcRows) {
    if (lrcRows != null) {
      forceFinished();
      mLastRow = -1;
      mCurRow = -1;
      mLrcRows.clear();
      mLrcRows.addAll(lrcRows);
      scrollTo(0, 0);
      invalidate();
    }
  }

  public void setLrcRows(List<LrcRow> lrcRows, int progress) {
    if (progress <= 0) {
      setLrcRows(lrcRows);
      return;
    }
    if (lrcRows != null) {
      forceFinished();
      mLastRow = -1;
      mCurRow = -1;
      mLrcRows.clear();
      mLrcRows.addAll(lrcRows);
      seekTo(progress, true);
      invalidate();
    }
  }

  @Override
  public void seekTo(int progress, boolean fromUser) {
    if (mLrcRows.size() <= 0) {
      return;
    }
    // 如果是由seekbar的进度改变触发 并且这时候处于拖动状态，则返回
    if (!fromUser && mCanDrag) {
      return;
    }
    int size = mLrcRows.size();
    for (int i = size - 1; i >= 0; i--) {
      LrcRow lrcRow = mLrcRows.get(i);
      if (progress >= lrcRow.getTime()) {
        if (mCurRow != i) {
          mLastRow = mCurRow;
          mCurRow = i;
          handleProgress(lrcRow, fromUser);
        }
        break;
      }
    }
  }

  private void handleProgress(LrcRow lrcRow, boolean fromUser) {
    if (mCanDrag) {
      invalidate();
      return;
    }
    int dstY = mCurRow * mRowHeight;
    if (fromUser) {
      forceFinished();
      scrollTo((int) scrollX, dstY);
      invalidate();
    } else {
      smoothScrollTo(dstY, DURATION_AUTO_SCROLL);
      // 如果高亮歌词的宽度大于View的宽，就需要开启属性动画，让它水平滚动
      float textWidth = mPaintCur.measureText(lrcRow.getContent());
      if (textWidth > mWidth) {
        startHorizontalScrollLrc(mWidth - textWidth, (long) (lrcRow.getTotalTime() * 0.6));
      }
    }
  }

  @Override
  public void setLrcScale(float factor) {
    if (factor < 0 || factor > 1) {
      return;
    }
    MIN_SCALE = 0.7f;
    MAX_SCALE = 1.7f;

    factor = MIN_SCALE + (MAX_SCALE - MIN_SCALE) * factor;
    mSizeText = SIZE_TEXT_DEFAULT * factor;
    mSizeTextCur = SIZE_TEXT_CUR_DEFAULT * factor;
    mSizeTime = SIZE_TIME_DEFAULT * factor;
    mPadding = PADDING_DEFAULT * factor;
    mPaddingTime = PADDING_TIME_DEFAULT * factor;
    LogUtil.debug(TAG,
        " factor :" + factor + "mSizeText : " + mSizeText + "mSizeTextCur " + mSizeTextCur
            + "mSizeTime:" + mSizeTime + "mPadding: " + mPadding + "mPaddingTime: " + mPaddingTime);
    resetValues();
    forceFinished();
    if (mCurRow != -1) {
      scrollTo((int) scrollX, mCurRow * mRowHeight);
    }
    invalidate();
  }

  private void resetValues() {
    mPaint.setTextSize((int) mSizeText);
    mPaintCur.setTextSize((int) mSizeTextCur);
    mPaintLine.setTextSize((int) mSizeTime);
    mOffsetY = (int) (getTextHeight(mPaintCur) / 2);
    mRowHeight = (int) (getTextHeight(mPaintCur) + mPadding);
  }

  @Override
  public void reset() {
    forceFinished();
    mLastRow = -1;
    mCurRow = -1;
    mLrcRows.clear();
    scrollTo(0, 0);
    invalidate();
  }

  private void startHorizontalScrollLrc(float endX, long duration) {
    if (mAnimation == null) {
      initAnim();
    } else {
      stopHorizontalScrollLrc();
    }
    mAnimation.setDuration(duration);
    // 延迟执行属性动画
    mAnimation.setDelay((long) (duration * 0.3));
    mAnimation.start();
  }

  private void stopHorizontalScrollLrc() {
    if (mAnimation != null) {
      mTextOffsetX = 0;
      mAnimation.cancel();
    }
  }

  public void setOnClickListener(OnClickListener l) {
    this.mOnClickListener = l;
  }

  public void setOnSeekChangeListener(OnSeekChangeListener l) {
    this.mOnSeekChangeListener = l;
  }

  public interface OnClickListener {
    void onClick();
  }

  public interface OnSeekChangeListener {
    void onProgressChanged(int progress);
  }

  static class UpdateListener implements AnimatorValue.ValueUpdateListener {
    private final WeakReference<LrcView> reference;

    UpdateListener(LrcView view) {
      this.reference = new WeakReference<>(view);
    }


    @Override
    public void onUpdate(AnimatorValue animatorValue, float v) {

      LrcView view = reference.get();
      if (view == null || view.mContext == null
          || view.mContext instanceof Ability && ((Ability) view.mContext).isTerminating()) {
        return;
      }
      view.mTextOffsetX = AnimatorValueUtils.getAnimatedValue(v, 0f, 1f) * -230;
      view.invalidate();

    }
  }
}
